#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <poll.h>
#include <netdb.h>

#ifdef BK_LINUX
#include <time.h>
#else
#include <sys/time.h>
#endif

#include "bkbase.h"

#include "bstcp.h"

#ifdef __cplusplus
extern "C" {
#endif

int BsSockOptOn(int sock, int level, int name)
{
   int i;

   i = 1;
   return setsockopt(sock, level, name, &i, sizeof(int));
}

int BsSockOptOff(int sock, int level, int name)
{
   int i;

   i = 0;
   return setsockopt(sock, level, name, &i, sizeof(int));
}

int BsCreateServerSocket(const char *IpAddr, int Port, int ListenQ)
{
   int r;
   int Sock;
   int Flag;
   struct sockaddr_in ServerAddr;

   Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   if( Sock < 0 )
   {
      return BS_EOTHER;
   }

   ServerAddr.sin_family = AF_INET;

   if ( IpAddr == NULL )
   {
      ServerAddr.sin_addr.s_addr = INADDR_ANY;
   }
   else
   {
      ServerAddr.sin_addr.s_addr  = inet_addr(IpAddr);
   }

   ServerAddr.sin_port = htons(Port);
   r = 1;
   setsockopt(Sock, SOL_SOCKET, SO_REUSEADDR, (char*)&r, sizeof(int));

   if ( bind(Sock, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) < 0 )
   {
      return BS_EOTHER;
   }

   if ( listen(Sock, ListenQ) < 0 )
   {
      return BS_EOTHER;
   }

   Flag = fcntl(Sock, F_GETFL, 0);

   if ( Flag == -1 )
   {
      return BS_EOTHER;
   }

   Flag |= O_NONBLOCK;

   if ( fcntl(Sock, F_SETFL, Flag) == -1 )
   {
      return BS_EOTHER;
   }

   return Sock;
}

int BsCreateClientSocket(const char *IpAddr, int Port, int MilliSecTimeOut)
{
   int r;
   int Flag;
   int Sock = -1;
   struct pollfd Poll;
   struct sockaddr_in ServerAddr;

   Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   if ( Sock < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   Flag = fcntl(Sock, F_GETFL, 0);

   if ( Flag < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   Flag |= O_NONBLOCK;

   if ( fcntl(Sock, F_SETFL, Flag) < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   ServerAddr.sin_family = AF_INET;

   if ( isdigit(IpAddr[0]) )
   {
      ServerAddr.sin_addr.s_addr  = inet_addr(IpAddr);
   }
   else
   {
      struct hostent *Host = gethostbyname(IpAddr);
      if ( Host == NULL )
      {
         r = BS_EOTHER;
         goto error;
      }
      ServerAddr.sin_addr = *((struct in_addr*)Host->h_addr_list[0]);
   }

   ServerAddr.sin_port = htons(Port);

   for ( ; ; )
   {
      int Error;
      socklen_t ErrorLen; 
      r = connect(Sock, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
      if ( r == 0 || errno == EISCONN )
      {
         break;
      }
      if ( r < 0 && errno != EINPROGRESS )
      {
         r = BS_EOTHER;
         goto error;
      }
      Poll.fd = Sock;
      Poll.events = POLLOUT;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         r = poll(&Poll, 1, MilliSecTimeOut);
      }
      if ( r == 0 )
      {
         r = BS_ETIMEOUT;
         goto error;
      }
      if ( r < 0 )
      {
         r = BS_EOTHER;
         goto error;
      }
      ErrorLen = sizeof(Error);
      if ( getsockopt(Sock, SOL_SOCKET, SO_ERROR, &Error, &ErrorLen) < 0 )
      {
         r = BS_EOTHER;
         goto error;
      }
      if ( Error != 0 )
      {
         r = BS_EOTHER;
         errno = Error;
         goto error;
      }
   }

   return Sock;

error :

   if ( Sock != -1 )
   {
      int ErrnoBak = errno;
      close(Sock);
      errno = ErrnoBak;
   }

   return r;
}

int BsAcceptConnection(int Sock, char *IpAddr, int MilliSecTimeOut)
{
   int r;
   struct sockaddr_in ServerAddr;
   int MsgSock;
   socklen_t Len;
   struct pollfd Poll;
   struct timeval StartTime;

   gettimeofday(&StartTime, NULL);

   for ( ; ; )
   {
      Poll.fd = Sock;
      Poll.events = POLLIN;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         struct timeval CurrTime;
         gettimeofday(&CurrTime, NULL);
         time_t Time = BkSubTimeval(&CurrTime, &StartTime);
         if ( Time < 0 )
         {
            Time = 0;
         }
         Time = MilliSecTimeOut - Time;
         if ( Time < 0 )
         {
            Time = 0;
         }
         r = poll(&Poll, 1, Time);
      }
      if ( r == 0 )
      {
         return BS_ETIMEOUT;
      }
      if ( r < 0 )
      {
         return BS_EOTHER;
      }
      Len = sizeof(ServerAddr);
      memset(&ServerAddr, 0, Len);
      errno = 0;
      MsgSock = accept(Sock, (struct sockaddr*)&ServerAddr, &Len);
      if ( MsgSock < 0 )
      {
         if ( errno == EAGAIN || errno == 0 )
         {
            continue;
         }
         return BS_EOTHER;
      }
      if ( IpAddr != NULL )
      {
         char TmpAddr[200 + 1];
         if ( inet_ntop(AF_INET, &ServerAddr.sin_addr, TmpAddr, 200) == NULL )
         {
            close(MsgSock);
            return BS_EOTHER;
         }
         BkStrNCpy(IpAddr, TmpAddr, 200);
      }
      break;
   }

   return MsgSock;
}

int BsCreateServerSocketUD(const char *Path, int ListenQ)
{
   int r;
   int Len;
   int Sock;
   int Flag;
   struct sockaddr_un ServerAddr;

   Sock = socket(AF_UNIX, SOCK_STREAM, 0);

   if ( Sock < 0 )
   {
      return BS_EOTHER;
   }

   unlink(Path);
   memset(&ServerAddr, 0, sizeof(ServerAddr));
   ServerAddr.sun_family = AF_UNIX;
   BkStrNCpy(ServerAddr.sun_path, Path, sizeof(ServerAddr.sun_path) - 1);
   Len = SUN_LEN(&ServerAddr);
#if defined(BK_AIX)
   ServerAddr.sun_len = Len;
#endif

   if ( bind(Sock, (struct sockaddr *)&ServerAddr, Len) < 0 )
   {
      return BS_EOTHER;
   }

   listen(Sock, ListenQ);

   Flag = fcntl(Sock, F_GETFL, 0);

   if ( Flag == -1 )
   {
      return BS_EOTHER;
   }

   Flag |= O_NONBLOCK;

   if ( fcntl(Sock, F_SETFL, Flag) == -1 )
   {
      return BS_EOTHER;
   }

   return Sock;
}

int BsCreateClientSocketUD(const char *Path, int MilliSecTimeOut)
{
   int r;
   int Len;
   int Flag;
   int Sock = -1;
   struct pollfd Poll;
   struct sockaddr_un ServerAddr;

   Sock = socket(AF_UNIX, SOCK_STREAM, 0);

   if ( Sock < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   Flag = fcntl(Sock, F_GETFL, 0);

   if ( Flag < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   Flag |= O_NONBLOCK;

   if ( fcntl(Sock, F_SETFL, Flag) < 0 )
   {
      r = BS_EOTHER;
      goto error;
   }

   memset(&ServerAddr, 0, sizeof(ServerAddr));
   ServerAddr.sun_family = AF_UNIX;
   BkStrNCpy(ServerAddr.sun_path, Path, sizeof(ServerAddr.sun_path) - 1);
   Len = SUN_LEN(&ServerAddr);
#if defined(BK_AIX)
   ServerAddr.sun_len = Len;
#endif
   
   r = connect(Sock, (struct sockaddr*)&ServerAddr, Len);

   if ( r < 0 )
   {
      if ( errno != EINPROGRESS )
      {
         r = BS_EOTHER;
         goto error;
      }
      Poll.fd = Sock;
      Poll.events = POLLIN;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         r = poll(&Poll, 1, MilliSecTimeOut);
      }
      if ( r == 0 )
      {
         r = BS_ETIMEOUT;
         goto error;
      }
      if ( r < 0 )
      {
         r = BS_EOTHER;
         goto error;
      }
      r = connect(Sock, (struct sockaddr*)&ServerAddr, Len);
      if ( r < 0 )
      {
         r = BS_EOTHER;
         goto error;
      }
   }

   return Sock;

error :

   if ( Sock >= 0 )
   {
      close(Sock);
   }

   return r;
}

int BsAcceptConnectionUD(int Sock, char *Path, int MilliSecTimeOut)
{
   int r;
   struct sockaddr_un ServerAddr;
   int MsgSock;
   socklen_t Len;
   struct pollfd Poll;
   struct timeval StartTime;

   gettimeofday(&StartTime, NULL);

   for ( ; ; )
   {
      Poll.fd = Sock;
      Poll.events = POLLIN;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         struct timeval CurrTime;
         gettimeofday(&CurrTime, NULL);
         time_t Time = BkSubTimeval(&CurrTime, &StartTime);
         if ( Time < 0 )
         {
            Time = 0;
         }
         Time = MilliSecTimeOut - Time;
         if ( Time < 0 )
         {
            Time = 0;
         }
         r = poll(&Poll, 1, Time);
      }
      if ( r == 0 )
      {
         return BS_ETIMEOUT;
      }
      if ( r < 0 )
      {
         return BS_EOTHER;
      }
      Len = sizeof(ServerAddr);
      memset(&ServerAddr, 0, Len);
      errno = 0;
      MsgSock = accept(Sock, (struct sockaddr*)&ServerAddr, &Len);
      if ( MsgSock < 0 )
      {
         if ( errno == EAGAIN || errno == 0 )
         {
            continue;
         }
         return BS_EOTHER;
      }
      if ( Path != NULL )
      {
         *((char*)(&ServerAddr) + Len) = '\0';
         BkStrNCpy(Path, ServerAddr.sun_path, sizeof(ServerAddr.sun_path) - 1);
      }
      break;
   }

   return MsgSock;
}

int BsReadFromSocket(int Sock, void *Buf, int Len, int MilliSecTimeOut)
{
   int i;
   int r;
   struct pollfd Poll;

   for ( i = 0; i < Len; )
   {
      Poll.fd = Sock;
      Poll.events = POLLIN;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         r = poll(&Poll, 1, MilliSecTimeOut);
      }
      if ( r == 0 )
      {
         return BS_ETIMEOUT;
      }
      if( r < 0 )
      {
         return BS_EOTHER;
      }
      r = read(Sock, (char*)Buf + i, Len - i);
      if ( r < 0 )
      {
         return BS_EOTHER;
      }
      if ( r == 0 )
      {
         return i;
      }
      i += r;
   }

   return i;
}

int BsReadFromSocketMaxLen(int Sock, void *Buf, int MaxLen, int MilliSecTimeOut)
{
   int i;
   int r;
   struct pollfd Poll;

   Poll.fd = Sock;
   Poll.events = POLLIN;
   Poll.revents = 0;

   if ( MilliSecTimeOut == BS_TIME_INFINITE )
   {
      r = poll(&Poll, 1, BS_TIME_INFINITE);
   }
   else
   {
      r = poll(&Poll, 1, MilliSecTimeOut);
   }

   if ( r == 0 )
   {
      return BS_ETIMEOUT;
   }

   if( r < 0 )
   {
      return BS_EOTHER;
   }

   r = read(Sock, (char*)Buf, MaxLen);

   if ( r < 0 )
   {
      return BS_EOTHER;
   }

   return r;
}

int BsWriteToSocket(int Sock, const void *Buf, int Len, int MilliSecTimeOut)
{
   int i;
   int r;
   struct pollfd Poll;

   for ( i = 0; i < Len; )
   {
      Poll.fd = Sock;
      Poll.events = POLLOUT;
      Poll.revents = 0;
      if ( MilliSecTimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         r = poll(&Poll, 1, MilliSecTimeOut);
      }
      if ( r == 0 )
      {
         return BS_ETIMEOUT;
      }
      if( r < 0 )
      {
         return BS_EOTHER;
      }
      r = write(Sock, (char*)Buf + i, Len - i);
      if ( r <= 0 )
      {
         return BS_EOTHER;
      }
      i += r;
   }

   return 0;
}

int BsWriteToSocket2(int Sock, const void *Buf, int Len, const void *Buf2, int Len2, int TimeOut)
{
   int i;
   int r;
   struct pollfd Poll;
   struct iovec IoVec[2];
   int TotalLen = Len + Len2;

   if ( Len2 == 0 )
   {
      return BsWriteToSocket(Sock, Buf, Len, TimeOut);
   }

   IoVec[0].iov_base = (void*)Buf;
   IoVec[0].iov_len = Len;
   IoVec[1].iov_base = (void*)Buf2;
   IoVec[1].iov_len = Len2;

   for ( i = 0; i < TotalLen; )
   {
      Poll.fd = Sock;
      Poll.events = POLLOUT;
      Poll.revents = 0;
      if ( TimeOut == BS_TIME_INFINITE )
      {
         r = poll(&Poll, 1, BS_TIME_INFINITE);
      }
      else
      {
         r = poll(&Poll, 1, TimeOut * 1000);
      }
      if ( r == 0 )
      {
         return BS_ETIMEOUT;
      }
      if( r < 0 )
      {
         return BS_EOTHER;
      }
      if ( i >= Len )
      {
         r = write(Sock, (char*)Buf2 + i - Len, TotalLen - i);
      }
      else
      {
         IoVec[0].iov_base = (char*)Buf + i;
         IoVec[0].iov_len = Len - i;
         r = writev(Sock, IoVec, 2);
      }
      if ( r < 0 && errno == EAGAIN )
      {
         continue;
      }
      if ( r <= 0 )
      {
         return BS_EOTHER;
      }
      i += r;
   }

   return 0;
}

int BsReadFromSocketHSL(int Sock, void *Buf, int MaxLen, int MilliSecTimeOut)
{
   char TmpStr[9];
   int r;
   int Len;

   r = BsReadFromSocket(Sock, TmpStr, 8, MilliSecTimeOut);

   if ( r <= 0 )
   {
      return r;
   }

   TmpStr[8] = BK_EOS;
   Len = BkHexStrToInt(TmpStr);

   if ( Len > MaxLen ){
      r = BsReadFromSocket(Sock, Buf, MaxLen, MilliSecTimeOut);
      if ( r < 0 )
      {
         return r;
      }
      return BK_E2BIG;
   }

   return BsReadFromSocket(Sock, Buf, Len, MilliSecTimeOut);
}

int BsWriteToSocketHSL(int Sock, const void *Buf, int Len, int MilliSecTimeOut)
{
   char TmpStr[80 + 1];
   int r;

   snprintf(TmpStr, 80, "%08x", Len);
   r = BsWriteToSocket(Sock, TmpStr, 8, MilliSecTimeOut);

   if ( r < 0 )
   {
      return r;
   }

   return BsWriteToSocket(Sock, Buf, Len, MilliSecTimeOut);
}

void BsSleep(int Millisecond)
{
   poll(NULL, 0, Millisecond);
}

#ifdef __cplusplus
}
#endif
